﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace CompositionPlugin
{
    public abstract class Runtime
    {
        public Runtime() : this(null) { }

        public Runtime(IEnumerable<Tuple<Type, IRuntimeService>> services)
        {
            if (services != null)
            {
                lock (this)
                {
                    foreach (var service in services)
                        this._serviceDict.Add(service.Item1, service.Item2);
                }
            }
        }

        #region runtime service
        /// <summary>
        /// Get service
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public T GetService<T>() where T : IRuntimeService
        {
            lock (this)
            {
                if (this._serviceDict.ContainsKey(typeof(T)))
                    return (T)this._serviceDict[typeof(T)];
                else
                    return (T)this.FailedToRetrieveService(typeof(T));
            }
        }
        /// <summary>
        /// Get service
        /// </summary>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        public object GetService(Type serviceType)
        {
            if(serviceType == null)
                throw new ArgumentNullException("serviceType");

            lock (this)
            {
                if (this._serviceDict.ContainsKey(serviceType))
                    return this._serviceDict[serviceType];
                else
                    return this.FailedToRetrieveService(serviceType);
            }
        }
        /// <summary>
        /// Set service
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="service"></param>
        public void SetService<T>(T service) where T : IRuntimeService
        {
            lock (this)
            {
                this._serviceDict[typeof(T)] = service;
            }
        }
        /// <summary>
        /// Set service
        /// </summary>
        /// <param name="serviceType"></param>
        /// <param name="instance"></param>
        public void SetService(Type serviceType, object instance)
        {
            if (!serviceType.GetInterfaces().Contains(typeof(IRuntimeService)))
                throw new ArgumentException("Service type must be subclass of IRuntimeService");
            if (instance == null)
                throw new ArgumentNullException("instance");
            if(!typeof(IRuntimeService).IsInstanceOfType(instance))
                throw new ArgumentException("Service must inherit from IRuntimeService");

            lock (this)
            {
                this._serviceDict[serviceType] = instance as IRuntimeService;
            }
        }
        /// <summary>
        /// All runtime services
        /// </summary>
        public IEnumerable<IRuntimeService> RuntimeServices
        {
            get
            {
                return this._serviceDict.Values.ToArray();
            }
        }

        protected virtual void InitializeAllService()
        {
            if (Interlocked.CompareExchange(ref this._initialized, 1, 0) == 0)
            {
                foreach (var service in this._serviceDict)
                    service.Value.Initialize();
            }
        }

        protected virtual object FailedToRetrieveService(Type serviceContractType)
        {
            return null;
        }

        private readonly Dictionary<Type, IRuntimeService> _serviceDict = new Dictionary<Type, IRuntimeService>();

        private int _initialized = 0;

        #endregion
    }
}
